{
 "cells": [
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "# Language Server Protocol (LSP) Integration Example\n",
    "\n",
    "The [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) is an open standard for communication between development tools and language servers.\n",
    "\n",
    "Specifically, the [Language Server for Kotlin](https://github.com/Kotlin/kotlin-lsp) provides features that integrate with JetBrains IDEs for Kotlin development.\n",
    "\n",
    "## Downloading the Language Server for Kotlin\n",
    "\n",
    "The [Language Server for Kotlin](https://github.com/Kotlin/kotlin-lsp) is available as a standalone distribution.\n",
    "Follow the instructions on the project page to download the latest version for your operating system.\n",
    "Make sure the `kotlin-lsp` executable is available on your `PATH`.\n",
    "\n",
    "## IntelliJ Platform Integration and Bundled Plugin\n",
    "\n",
    "Declare the IntelliJ Platform integration to configure the necessary libraries."
   ]
  },
  {
   "metadata": {
    "collapsed": true,
    "ExecuteTime": {
     "end_time": "2025-08-07T11:52:31.143740Z",
     "start_time": "2025-08-07T11:52:30.834106Z"
    }
   },
   "cell_type": "code",
   "source": "%use intellij-platform",
   "outputs": [
    {
     "data": {
      "text/plain": [
       "IntelliJ Platform integration is loaded"
      ]
     },
     "metadata": {},
     "output_type": "display_data",
     "jetTransient": {
      "display_id": null
     }
    }
   ],
   "execution_count": 1
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "The Language Server Protocol support is already available out-of-the-box in commercial JetBrains IDEs."
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "## Integrating the Server with Kotlin Files\n",
    "\n",
    "To integrate the Kotlin LSP, two components are required:\n",
    "\n",
    "1. An LSP server descriptor that indicates supported files and provides a command line to start the server in a standalone process.\n",
    "2. An LSP support provider that ensures the server is available for a given file.\n",
    "\n",
    "### Server Descriptor\n",
    "\n",
    "Create a server descriptor that supports files with the `.kt` extension.\n",
    "To start the LSP server, create a command line that runs the `kotlin-lsp` binary (available on your `PATH`) with the `--stdio` switch, which enables communication over standard input and output streams."
   ]
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-07T11:52:34.335310Z",
     "start_time": "2025-08-07T11:52:34.275844Z"
    }
   },
   "cell_type": "code",
   "source": [
    "import com.intellij.execution.configurations.GeneralCommandLine\n",
    "import com.intellij.openapi.project.Project\n",
    "import com.intellij.openapi.vfs.VirtualFile\n",
    "import com.intellij.platform.lsp.api.ProjectWideLspServerDescriptor\n",
    "\n",
    "class KotlinLspServerDescriptor(project: Project) : ProjectWideLspServerDescriptor(project, \"KN Kotlin LSP\") {\n",
    "    override fun isSupportedFile(file: VirtualFile) = file.extension == \"kt\"\n",
    "\n",
    "    override fun createCommandLine() = GeneralCommandLine(\"kotlin-lsp\", \"--stdio\")\n",
    "}"
   ],
   "outputs": [],
   "execution_count": 2
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "## LSP Support Provider\n",
    "\n",
    "Create an LSP support provider that starts the LSP server, if necessary, using the corresponding server descriptor.\n"
   ]
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-07T11:32:07.929902Z",
     "start_time": "2025-08-07T11:32:07.897764Z"
    }
   },
   "cell_type": "code",
   "source": [
    "import com.intellij.platform.lsp.api.LspServerSupportProvider\n",
    "\n",
    "class KotlinLspServerSupportProvider : LspServerSupportProvider {\n",
    "    override fun fileOpened(project: Project, file: VirtualFile, serverStarter: LspServerSupportProvider.LspServerStarter) {\n",
    "        if (file.extension == \"kt\") {\n",
    "            serverStarter.ensureServerStarted(KotlinLspServerDescriptor(project))\n",
    "        }\n",
    "    }\n",
    "}"
   ],
   "outputs": [],
   "execution_count": 4
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "## Registering an Extension Point\n",
    "\n",
    "`KotlinLspServerSupportProvider` is an implementation that must be registered under the corresponding extension point.\n",
    "We’ll represent the plugin descriptor extension declaration in Kotlin code.\n",
    "\n",
    "A full-fledged plugin would declare the following in `plugin.xml`:\n",
    "\n",
    "```xml\n",
    "<extensions defaultExtensionNs=\"com.intellij\">\n",
    "    <platform.lsp.serverSupportProvider implementation=\"com.example.KotlinLspServerSupportProvider\"/>\n",
    "</extensions>\n",
    "```\n",
    "\n",
    "In this Kotlin Notebook, an XML descriptor is not required.\n",
    "Instead, create an instance of the LSP server support provider and register it under the corresponding extension point."
   ]
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-07T11:32:07.982226Z",
     "start_time": "2025-08-07T11:32:07.957526Z"
    }
   },
   "cell_type": "code",
   "source": [
    "import com.intellij.platform.lsp.api.LspServerManager\n",
    "import com.intellij.platform.lsp.api.LspServerSupportProvider\n",
    "\n",
    "registerExtension(LspServerSupportProvider.EP_NAME, KotlinLspServerSupportProvider())"
   ],
   "outputs": [],
   "execution_count": 5
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "## Testing the Integration\n",
    "\n",
    "To test the integration:\n",
    "1. Install [Kotlin LSP CLI tool](https://github.com/Kotlin/kotlin-lsp?tab=readme-ov-file#install-kotlin-lsp-cli).\n",
    "2. Run this Kotlin Notebook.\n",
    "3. Create a Kotlin file with some unnecessary constructs.\n",
    "\n",
    "   ```kotlin\n",
    "   import java.util.List\n",
    "\n",
    "   class Hello {\n",
    "       fun sayHello() = \"Hello, world!\"\n",
    "   }\n",
    "   ```\n",
    "4. Observe two warnings:\n",
    "   - an unnecessary import,\n",
    "   - an implicit return type for the `sayHello` function.\n",
    "\n",
    "Use [Intention Actions](https://www.jetbrains.com/help/idea/intention-actions.html) and note two actions provided by the LSP server:\n",
    "\n",
    "- *Organize Imports* removes the unnecessary `import java.util.List`.\n",
    "- *Specify type explicitly* adds an explicit `String` return type to `sayHello`.\n",
    "\n",
    "These actions may be duplicated by native intention actions provided by JetBrains IDEs such as *Organize imports* or *Specify return type explicitly*."
   ]
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "## Summary\n",
    "\n",
    "- Load the `com.intellij.modules.ultimate` plugin to enable LSP functionality.\n",
    "- Register a `LspServerSupportProvider` implementation under the `com.intellij.platform.lsp.serverSupportProvider` extension point.\n",
    "- Create a `ProjectWideLspServerDescriptor` subclass to launch the LSP server in a standalone process, using standard input and output streams as the communication channel.\n",
    "- Verify the functionality via the Intention Actions list."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Kotlin",
   "language": "kotlin",
   "name": "kotlin"
  },
  "language_info": {
   "name": "kotlin",
   "version": "2.2.20-Beta2",
   "mimetype": "text/x-kotlin",
   "file_extension": ".kt",
   "pygments_lexer": "kotlin",
   "codemirror_mode": "text/x-kotlin",
   "nbconvert_exporter": ""
  },
  "ktnbPluginMetadata": {
   "sessionRunMode": "IDE_PROCESS"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
